package com.itaem.community.core.utils;

import cn.hutool.core.util.RandomUtil;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTCreationException;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.io.Serializable;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Date;
import java.util.Map;

import static com.itaem.community.core.constant.JwtConstant.*;

/**
 * <p>项目名称: dollars </p>
 * <p>文件名称: JWTUtils </p>
 * <p>描述: [类型描述] </p>
 * <p>创建时间: 2020/3/18 </p>
 * <p>作者信息: fanwe</p>
 *
 * @author tomato
 * @version v1.0
 */
@Slf4j
@Component
public class JwtUtils implements Serializable {

    /**
     * 校验token的正确性
     *
     * @param token      密钥
     * @param username   登录账户
     * @param credential 登录密钥
     * @return 校验结果
     */
    public static boolean verify(String token, String username, String credential) {
        try {
            // 使用算法
            Algorithm algorithm = Algorithm.HMAC384(credential);
            // 创建JWT校验器，并对token进行校验
            JWT.require(algorithm)
                    .withClaim(USER_NAME, username)
                    .withClaim(CREDENTIAL, credential)
                    .build()
                    .verify(token);
            return true;
        } catch (JWTVerificationException ve) {
            log.error(ve.getMessage(), ve);
            return false;
        }
    }

    /**
     * 获得token中的username信息
     *
     * @param token 用户的信息凭证
     * @return token中的user id信息
     */
    public static String getUserIdFromToken(String token) {
        try {
            DecodedJWT jwt = JWT.decode(token);
            return jwt.getClaim(USER_ID).asString();
        } catch (JWTDecodeException de) {
            log.warn(de.getMessage(), de.getCause());
            return null;
        }
    }

    /**
     * 获得token中的username信息
     *
     * @param token 用户的信息凭证
     * @return token中的username信息
     */
    public static String getUserNameFromToken(String token) {
        try {
            DecodedJWT jwt = JWT.decode(token);
            return jwt.getClaim(USER_NAME).asString();
        } catch (JWTDecodeException de) {
            return null;
        }
    }

    /**
     * 生成token，在指定时间后过期
     *
     * @param userId     用户id
     * @param username   登录名
     * @param credential 登录凭证
     * @return 加密后的token
     */
    public static String generateToken(String userId, String username, String credential) {
        try {
            Algorithm algorithm = Algorithm.HMAC384(credential);
            // 附带上user信息
            return JWT.create()
                    // 加盐，防止token被复制
                    .withClaim(TOKEN_SALT_KEY, RandomUtil.randomString(20))
                    .withClaim(USER_NAME, username)
                    .withClaim(USER_ID, userId)
                    .withExpiresAt(new Date(
                                    // 用LocalDateTime的方式来取毫秒时
                                    LocalDateTime
                                            .now()
                                            .plusMinutes(TOKEN_EXPIRE_TIME_MINUTE)
                                            .toInstant(ZoneOffset.ofHours(+8))
                                            .toEpochMilli()
                            )
                    )
                    .sign(algorithm);
        } catch (JWTCreationException ce) {
            log.error(ce.getMessage(), ce);
            return null;
        }
    }

    /**
     * 根据claims生成在一定时间后过期的指定token
     *
     * @param claims 信息
     * @param secret 密钥
     * @return token
     */
    public static String generateToken(Map<String, Object> claims, String secret) {
        try {
            JWTCreator.Builder builder = JWT.create();
            claims.forEach((key, val) -> builder.withClaim(key, val.toString()));
            builder.withExpiresAt(
                    new Date(
                            // 用LocalDateTime的方式来取毫秒时
                            LocalDateTime
                                    .now()
                                    .plusMinutes(TOKEN_EXPIRE_TIME_MINUTE)
                                    .toInstant(ZoneOffset.ofHours(+8))
                                    .toEpochMilli()
                    )
            );
            return builder.sign(Algorithm.HMAC384(secret));
        } catch (JWTCreationException ce) {
            log.error(ce.getMessage(), ce);
            return null;
        }
    }
}
